package org.dendy.app.texteditor;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.LinkedList;

import org.eclipse.swt.*;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.ExtendedModifyEvent;
import org.eclipse.swt.custom.ExtendedModifyListener;
import org.eclipse.swt.custom.LineStyleEvent;
import org.eclipse.swt.custom.LineStyleListener;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.printing.*;
import org.eclipse.swt.widgets.*;

public class Item {

	private Shell shell;

	private StyledText text;

	private Menu bar;

	private ToolBar tBar;

	private ToolItem tItem;

	private Label label;

	String keyword = " ";

	LinkedList<StyleRange> list;

	LinkedList<Object> undoStack = new LinkedList<Object>();

	LinkedList<Object> redoStack = new LinkedList<Object>();

	private static final int MAX_STACK_SIZE = 1000;

	CTabFolder tabFolder;

	CTabItem item;

	protected Item(Shell shell) {
		this.shell = shell;
		bar = new Menu(shell, SWT.BAR);
	}

	/**
	 * Membuat tampilan text editor
	 * 
	 * @param display
	 */

	void createStyledText(Display display) {
		text = new StyledText(shell, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL
				| SWT.H_SCROLL);
		text.setLayoutData(new GridData(GridData.FILL_BOTH));
		// Fungsi untuk melakukan update pada statusbar
		text.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				updateStatusBar();
			}
		});
		// Fungsi untuk membuat statusbar
		createStatusBar();

		// Fungsi untuk update status bar
		updateStatusBar();

		// Fungsi untuk keluar dari shell
		closeEditor();

		// Fungsi untuk Undo di StyledText
		createUndo();

		// Fungsi untuk Find Text pada shell
		createFind();
	}

	/** Membuat MenuBar */
	void createMenuBar() {

		shell.setMenuBar(bar);

		// menu Item File
		MenuItem fileItem = new MenuItem(bar, SWT.CASCADE);
		fileItem.setText("File");
		fileItem.setMenu(createMenuFile());

		// Menu Item Edit
		MenuItem editItem = new MenuItem(bar, SWT.CASCADE);
		editItem.setText("Edit");
		editItem.setMenu(createEditMenu());

		// Menu Item View
		MenuItem viewItem = new MenuItem(bar, SWT.CASCADE);
		viewItem.setText("View");
		viewItem.setMenu(createView());

		// Menu Item Search
		MenuItem searchItem = new MenuItem(bar, SWT.CASCADE);
		searchItem.setText("Search");
		searchItem.setMenu(createSearch());

		// Menu Item Help
		MenuItem helpItem = new MenuItem(bar, SWT.CASCADE);
		helpItem.setText("Help");
		helpItem.setMenu(createHelp());
	}

	/**
	 * Membuat MenuBar
	 * 
	 * @return menu
	 */
	Menu createMenuFile() {
		bar = shell.getMenuBar();
		Menu menu = new Menu(bar);

		// File->New
		MenuItem item = new MenuItem(menu, SWT.PUSH);
		item.setText("&New\t\tCtrl+N");
		item.setAccelerator(SWT.MOD1 + 'N');
		item.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event event) {
				text.setText("");
			}
		});

		// File->Open
		item = new MenuItem(menu, SWT.PUSH);
		item.setText("&Open..\t\tCtrl+O");
		item.setAccelerator(SWT.MOD1 + 'O');
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				openFile();
			}
		});

		// File->Save
		item = new MenuItem(menu, SWT.PUSH);
		item.setText("&Save..\t\tCtrl+S");
		item.setAccelerator(SWT.MOD1 + 'S');
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				saveFile();
			}
		});

		new MenuItem(menu, SWT.SEPARATOR);

		// File->Print
		item = new MenuItem(menu, SWT.PUSH);
		item.setText("&Print..\t\tCtrl+P");
		item.setAccelerator(SWT.MOD1 + 'P');
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				printFile();
			}
		});

		new MenuItem(menu, SWT.SEPARATOR);

		// File->Quit
		item = new MenuItem(menu, SWT.PUSH);
		item.setText("&Quit\t\tCtrl+Q");
		item.setAccelerator(SWT.MOD1 + 'Q');
		item.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event event) {
				if ("".equals(text.getText())) {
					shell.dispose();
				} else {
					quit();
				}
			}
		});

		return menu;
	}

	/**
	 * Create Menu Item File
	 * 
	 * @return menu
	 */
	Menu createEditMenu() {
		bar = shell.getMenuBar();
		Menu menu = new Menu(bar);

		MenuItem item = new MenuItem(menu, SWT.PUSH);

		// File->Undo
		item.setText(" &Undo\t\tCtrl+Z ");
		item.setAccelerator(SWT.MOD1 + 'Z');
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				undo();
			}
		});

		// File->Rendo
		item = new MenuItem(menu, SWT.PUSH);
		item.setText(" &Redo\t\tCtrl+R ");
		item.setAccelerator(SWT.MOD1 + 'R');
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				redo();
			}
		});

		new MenuItem(menu, SWT.SEPARATOR);

		// File->Cut
		item = new MenuItem(menu, SWT.PUSH);
		item.setText(" &Cut\t\tCtrl+X ");
		item.setAccelerator(SWT.MOD1 + 'X');
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				text.cut();
			}
		});

		// File->Copy
		item = new MenuItem(menu, SWT.PUSH);
		item.setText(" &Copy\t\tCtrl+C ");
		item.setAccelerator(SWT.MOD1 + 'X');
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				text.copy();
			}
		});

		// File->Paste
		item = new MenuItem(menu, SWT.PUSH);
		item.setText(" &Paste\t\tCtrl+V ");
		item.setAccelerator(SWT.MOD1 + 'V');
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				text.paste();
			}
		});

		new MenuItem(menu, SWT.SEPARATOR);

		// File->SelectAll
		item = new MenuItem(menu, SWT.PUSH);
		item.setText(" &SelectAll\t\tCtrl+A ");
		item.setAccelerator(SWT.MOD1 + 'A');
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				text.selectAll();
			}
		});

		return menu;
	}

	// membuat menu item view
	Menu createView() {
		Menu bar = shell.getMenuBar();
		Menu menu = new Menu(bar);

		// View and Hidden toolbar
		final MenuItem item = new MenuItem(menu, SWT.CHECK);
		item.setSelection(true);
		item.setText("&Toolbar");

		Listener listener = new Listener() {
			public void handleEvent(Event event) {
				if (item.getSelection()) {
					tBar.setVisible(true);
				} else {
					tBar.setVisible(false);
				}

			}
		};

		item.addListener(SWT.Selection, listener);

		// View and Hidden Statusbar
		final MenuItem itemSec = new MenuItem(menu, SWT.CHECK);
		itemSec.setSelection(true);
		itemSec.setText("&Statusbar");

		Listener listener2 = new Listener() {
			public void handleEvent(Event event) {
				if (itemSec.getSelection()) {
					label.setVisible(true);
				} else {
					label.setVisible(false);
				}

			}
		};
		itemSec.addListener(SWT.Selection, listener2);

		return menu;

	}

	// Membuat menu item search
	Menu createSearch() {
		Menu bar = shell.getMenuBar();
		Menu menu = new Menu(bar);
		MenuItem item = new MenuItem(menu, SWT.PUSH);
		item.setText("Find Text");
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				findString();
			}
		});
		return menu;
	}

	// membuat menu item help
	Menu createHelp() {
		Menu bar = shell.getMenuBar();
		Menu menu = new Menu(bar);
		MenuItem item = new MenuItem(menu, SWT.PUSH);
		item.setText("About");
		item.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				about();
			}
		});
		return menu;
	}

	/**
	 * Membuat Toolbar
	 * 
	 * @param display
	 */
	void createToolBar(Display display) {

		Image newOpen = new Image(display, "image/document-new.png");
		Image open = new Image(display, "image/document-open.png");
		Image save = new Image(display, "image/document-save.png");
		Image print = new Image(display, "image/print.png");
		Image undo = new Image(display, "image/edit-undo.png");
		Image redo = new Image(display, "image/edit-redo.png");
		Image cut = new Image(display, "image/edit-cut.png");
		Image copy = new Image(display, "image/copy.png");
		Image paste = new Image(display, "image/edit-paste.png");
		Image search = new Image(display, "image/search.png");

		// Image New
		tBar = new ToolBar(shell, SWT.FLAT | SWT.HORIZONTAL);
		tItem = new ToolItem(tBar, SWT.PUSH);
		tItem.setImage(newOpen);
		tItem.setToolTipText("New");
		tItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				text.setText("");
			}
		});

		// Image Open
		ToolItem itemOpen = new ToolItem(tBar, SWT.PUSH | SWT.FLAT);
		itemOpen.setImage(open);
		itemOpen.setToolTipText("Open");
		itemOpen.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				openFile();
			}
		});

		// Image Save
		ToolItem itemSave = new ToolItem(tBar, SWT.PUSH);
		itemSave.setImage(save);
		itemSave.setToolTipText("Save");
		itemSave.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				saveFile();
			}
		});

		// Pembatas antara image
		new ToolItem(tBar, SWT.SEPARATOR);

		// Print
		ToolItem itemPrint = new ToolItem(tBar, SWT.PUSH);
		itemPrint.setImage(print);
		itemPrint.setToolTipText("Print");
		itemPrint.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				printFile();
			}
		});

		// Pembatas antara image
		new ToolItem(tBar, SWT.SEPARATOR);

		// Image undo
		ToolItem itemUndo = new ToolItem(tBar, SWT.PUSH);
		itemUndo.setImage(undo);
		itemUndo.setToolTipText("Undo");
		itemUndo.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				undo();
			}
		});

		// Image rendo
		ToolItem itemRendo = new ToolItem(tBar, SWT.PUSH);
		itemRendo.setImage(redo);
		itemRendo.setToolTipText("Redo");
		itemRendo.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				redo();
			}
		});

		// Pembatas antara image
		new ToolItem(tBar, SWT.SEPARATOR);

		// Image Cut
		ToolItem itemCut = new ToolItem(tBar, SWT.PUSH);
		itemCut.setImage(cut);
		itemCut.setToolTipText("Cut");
		itemCut.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				text.cut();
			}
		});

		// Image Copy
		ToolItem itemCopy = new ToolItem(tBar, SWT.PUSH);
		itemCopy.setImage(copy);
		itemCopy.setToolTipText("Copy");
		itemCopy.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				text.copy();
			}
		});

		// Image Paste
		ToolItem itemPaste = new ToolItem(tBar, SWT.PUSH);
		itemPaste.setImage(paste);
		itemPaste.setToolTipText("Paste");
		itemPaste.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				text.paste();
			}
		});

		new ToolItem(tBar, SWT.SEPARATOR);

		// Image
		ToolItem itemSearch = new ToolItem(tBar, SWT.PUSH);
		itemSearch.setImage(search);
		itemSearch.setToolTipText("Search");
		itemSearch.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				findString();
			}
		});
	}

	/** close the text editor */
	private void closeEditor() {
		shell.addListener(SWT.Close, new Listener() {
			public void handleEvent(Event event) {
				int style = SWT.APPLICATION_MODAL | SWT.YES | SWT.NO;
				MessageBox messageBox = new MessageBox(shell, style);
				messageBox.setText("Information");
				messageBox.setMessage("Close the SWediT?");
				event.doit = messageBox.open() == SWT.YES;
			}
		});
	}

	/** StatusBar */
	private void createStatusBar() {
		label = new Label(shell, SWT.BORDER | SWT.RIGHT_TO_LEFT);
		label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
	}

	/**
	 * Method openFile() akan dipanggil di toolbar image open dan dipanggil di
	 * MenuItem
	 */
	private void openFile() {
		final FileDialog dialog = new FileDialog(shell, SWT.OPEN);
		dialog.open();
		String filename = dialog.getFilterPath() + File.separator
				+ dialog.getFileName();
		File file = new File(filename);
		if (file.isFile()) {
			BufferedReader br;
			try {
				br = new BufferedReader(new FileReader(file));
				StringBuffer buf = new StringBuffer();
				String line = null;
				while ((line = br.readLine()) != null) {
					buf.append(line + '\n');
				}
				br.close();
				text.setText(buf.toString());
			} catch (FileNotFoundException e1) {
				e1.printStackTrace();
			} catch (IOException e2) {
				e2.printStackTrace();
			}
		}
	}

	/**
	 * Method saveFile() akan dipanggil di toolbar image open dan dipanggil di
	 * MenuItem
	 */
	private boolean saveFile() {
		final FileDialog dialog = new FileDialog(shell, SWT.SAVE);
		dialog.setFilterExtensions(new String[] { "*.txt" });
		String fn = dialog.open();
		if (fn != null) {
			File file = new File(fn);
			FileWriter fileWriter;
			try {
				fileWriter = new FileWriter(file);
				fileWriter.write(text.getText());
				fileWriter.close();
				return true;
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
		return false;
	}

	/**
	 * Method printFile() akan dipanggil di toolbar image open dan dipanggil di
	 * MenuItem
	 */
	private void printFile() {
		final PrintDialog printDialog = new PrintDialog(shell, SWT.NONE);
		PrinterData printerData = printDialog.open();
		if (!(printerData == null)) {
			Printer p = new Printer(printerData);
			p.startJob("PrintJob");
			p.startPage();
			Rectangle trim = p.computeTrim(0, 0, 0, 0);
			Point dpi = p.getDPI();
			int leftMargin = dpi.x + trim.x;
			int topMargin = dpi.y / 2 + trim.y;
			GC gc = new GC(p);
			Font font = gc.getFont();
			String printText = text.getText();
			gc.drawString(printText, leftMargin, topMargin
					+ font.getFontData()[0].getHeight());
			p.endPage();
			gc.dispose();
			p.endJob();
			p.dispose();
		}
	}

	/** About Text editor */
	private void about() {
		MessageBox mb = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
		mb.setMessage("SWT TextEditor\n" + "Februari 2011\n"
				+ "license : GPL\n" + "aryandie@andiasoft.com");
		mb.open();
	}

	/** Close Shell */
	private void quit() {
		if (!"".equals(text.getText())) {
			MessageBox box = new MessageBox(shell, SWT.YES | SWT.NO
					| SWT.CANCEL);
			box.setMessage("File is modified. Do you want to save?");
			int result = box.open();
			if (result == SWT.YES) {
				saveFile();
			} else {
				shell.dispose();
			}
		}
	}

	/** Fungsi untuk menghitung colomn yang dipanggil oleh fungsi updateStatus() */
	private int getColumn() {
		int pos, line;
		pos = text.getCaretOffset();
		line = text.getLineAtOffset(pos);
		return pos - text.getOffsetAtLine(line) + 1;
	}

	/** Fungsi untuk menghitung line yang dipanggil oleh fungsi updateStatus() */
	private int getLineText() {
		int line;
		line = text.getCaretOffset();
		return text.getLineAtOffset(line) + 1;
	}

	/** Update statusbar */
	private void updateStatusBar() {
		StringBuffer buffer = new StringBuffer();
		// untuk menampilkan colomn keberapa
		buffer.append("\tCol: ");
		buffer.append(getColumn());
		// untuk menampilkan line keberapa
		buffer.append("\tLn: ");
		buffer.append(getLineText());
		label.setText(" http://aryandie.blogspot.com " + buffer.toString());
	}

	/** Fungsi untuk mencari line didalam text editor */
	private void findString() {
		createFind();
		Shell s = new Shell(shell);
		final Text t;
		s.setLayout(new GridLayout(2, false));
		s.setText("Find Text");
		t = new Text(s, SWT.SINGLE | SWT.BORDER);
		Button button = new Button(s, SWT.PUSH);
		button.setText("Search");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				keyword = t.getText();
				text.redraw();
			}
		});

		s.pack();
		s.open();
	}

	/** Fungsi untuk memberikan warna pada find Text */
	private StyleRange getHighlightStyle(int startOffset, int length) {
		StyleRange styleRange = new StyleRange();
		styleRange.start = startOffset;
		styleRange.length = length;
		styleRange.background = shell.getDisplay().getSystemColor(
				SWT.COLOR_YELLOW);
		return styleRange;
	}

	/** Text find */
	private void createFind() {
		text.addLineStyleListener(new LineStyleListener() {
			public void lineGetStyle(LineStyleEvent event) {
				if (keyword == null || keyword.length() == 0) {
					event.styles = new StyleRange[0];
					return;
				}

				String line = event.lineText;
				int cursor = -1;

				list = new LinkedList<StyleRange>();
				while ((cursor = line.indexOf(keyword, cursor + 1)) >= 0) {
					list.add(getHighlightStyle(event.lineOffset + cursor,
							keyword.length()));
				}

				event.styles = (StyleRange[]) list.toArray(new StyleRange[list
						.size()]);
			}
		});
	}

	/** Undo */
	private void createUndo() {
		// Fungsi untuk Undo
		text.addExtendedModifyListener(new ExtendedModifyListener() {
			public void modifyText(ExtendedModifyEvent event) {
				String currText = text.getText();
				String newText = currText.substring(event.start, event.start
						+ event.length);
				if (newText != null && newText.length() > 0) {
					if (undoStack.size() == MAX_STACK_SIZE) {
						undoStack.remove(undoStack.size() - 1);
					}
					undoStack.add(0, newText);
				}
			}
		});
	}

	/** Fungsi untuk Undo pada text editor */
	private void undo() {
		if (undoStack.size() > 0) {
			String lastEdit = (String) undoStack.remove(0);
			int editLength = lastEdit.length();
			String currText = text.getText();
			int startReplaceIndex = currText.length() - editLength;
			text.replaceTextRange(startReplaceIndex, editLength, "");
		}
	}

	/** Fungsi untuk Redo pada text editor */
	private void redo() {
		if (redoStack.size() > 0) {
			String textString = (String) redoStack.remove(0);
			moveCursorToEnd();
			text.append(textString);
			moveCursorToEnd();
		}
	}

	/** Fungsi tambahan untuk fungsi redo */
	private void moveCursorToEnd() {
		text.setCaretOffset(text.getText().length());
	}
}
